package com.qingsong.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.mybatisflex.core.query.QueryChain;
import com.mybatisflex.core.relation.RelationManager;
import com.mybatisflex.core.update.UpdateChain;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.qingsong.constant.ML;
import com.qingsong.dto.CourseInsertDTO;
import com.qingsong.dto.CoursePageDTO;
import com.qingsong.dto.CourseUpdateDTO;
import com.qingsong.entity.Course;
import com.qingsong.exception.ServiceException;
import com.qingsong.mapper.CourseMapper;
import com.qingsong.mapper.EpisodeMapper;
import com.qingsong.mapper.SeasonMapper;
import com.qingsong.service.CourseService;
import com.qingsong.util.MinioUtil;
import com.qingsong.vo.CourseSimpleListVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDateTime;
import java.util.List;
import static com.qingsong.entity.table.CourseTableDef.COURSE;
import static com.qingsong.entity.table.EpisodeTableDef.EPISODE;
import static com.qingsong.entity.table.SeasonTableDef.SEASON;

/**
 * 课程表 服务层实现。
 *
 * @author qingsong
 * @since 2024-12-22
 */
@Service
@CacheConfig(cacheNames = "course")
public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course>  implements CourseService{


    @Autowired
    private SeasonMapper seasonMapper;

    @Autowired
    private EpisodeMapper episodeMapper;

    @CacheEvict(allEntries = true)
    @Override
    public boolean insert(CourseInsertDTO dto) {
        // 标题查重
        if (QueryChain.of(mapper)
                .where(COURSE.TITLE.eq(dto.getTitle()))
                .exists()) {
            throw new ServiceException("课程标题已存在");
        }
        // 组装 entity 实体类
        Course course = BeanUtil.copyProperties(dto, Course.class);
        course.setSummary(ML.Course.DEFAULT_SUMMARY);
        course.setCover(ML.Course.DEFAULT_COVER);
        course.setCreated(LocalDateTime.now());
        course.setUpdated(LocalDateTime.now());
        return mapper.insert(course) > 0;
    }

    @Cacheable(key = "#p0", condition = "#p0 != null", unless = "#result == null")
    @Override
    public Course select(Long id) {
        RelationManager.addQueryRelations("category", "seasons", "episodes");
        return mapper.selectOneWithRelationsById(id);
    }

    @Override
    @Cacheable(key = "#root.methodName", unless = "#result == null")
    public List<CourseSimpleListVO> simpleList() {
        return QueryChain.of(mapper)
                .orderBy(COURSE.ID.desc(), COURSE.IDX.asc())
                .withRelations()
                .listAs(CourseSimpleListVO.class);
    }

    @Cacheable(key = "#root.methodName + ':' + #p0.toString()", condition = "#p0 != null", unless = "#result == null")
    @Override
    public Page<Course> page(CoursePageDTO dto) {
        RelationManager.addQueryRelations("category", "seasons", "episodes");
        QueryChain<Course> queryChain = QueryChain.of(mapper)
                .orderBy(COURSE.IDX.asc(), COURSE.ID.desc());
        // title 条件
        String title = dto.getTitle();
        if (ObjectUtil.isNotNull(title)) {
            queryChain.where(COURSE.TITLE.like(title));
        }
        // categoryId 条件
        Long categoryId = dto.getFkCategoryId();
        if (ObjectUtil.isNotNull(categoryId)) {
            queryChain.where(COURSE.FK_CATEGORY_ID.eq(categoryId));
        }
        return queryChain.withRelations()
                .page(new Page<>(dto.getPageNum(), dto.getPageSize()));
    }

    @CacheEvict(allEntries = true)
    @Override
    public boolean update(CourseUpdateDTO dto) {
        // 标题查重
        if (QueryChain.of(mapper)
                .where(COURSE.TITLE.eq(dto.getTitle()))
                .and(COURSE.ID.ne(dto.getId()))
                .exists()) {
            throw new ServiceException("课程标题已存在");
        }
        // 组装 entity 实体类
        Course course = BeanUtil.copyProperties(dto, Course.class);
        course.setUpdated(LocalDateTime.now());
        return UpdateChain.of(course)
                .where(COURSE.ID.eq(course.getId()))
                .update();
    }

    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(allEntries = true)
    @Override
    public boolean delete(Long id) {
        // 通过课程主键查询全部季次的ID列表
        List<Long> seasonIds = QueryChain.of(seasonMapper)
                .select(SEASON.ID)
                .where(SEASON.FK_COURSE_ID.eq(id))
                .objListAs(Long.class);
        // 存在季次记录时，批量删除季次和集次
        this.clearSeasonAndEpisode(seasonIds);
        // 删除课程
        return mapper.deleteById(id) > 0;
    }

    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(allEntries = true)
    @Override
    public boolean deleteBatch(List<Long> ids) {
        // 通过课程主键列表批量查询全部季次的ID列表
        List<Long> seasonIds = QueryChain.of(seasonMapper)
                .select(SEASON.ID)
                .where(SEASON.FK_COURSE_ID.in(ids))
                .objListAs(Long.class);
        // 存在季次记录时，批量删除季次和集次
        this.clearSeasonAndEpisode(seasonIds);
        // 批量删除课程
        return mapper.deleteBatchByIds(ids) > 0;
    }

    /**
     * 根据季次ID列表，清空全部季次记录及每个季次中的集次记录
     *
     * @param seasonIds 季次主键列表
     */
    private void clearSeasonAndEpisode(List<Long> seasonIds) {
        if (ObjectUtil.isNotEmpty(seasonIds)) {
            // 删除季次记录
            if (seasonMapper.deleteBatchByIds(seasonIds) <= 0) {
                throw new ServiceException("季次删除失败");
            }

            // 通过季次主键列表查询全部集次的ID列表
            List<Long> episodeIds = episodeMapper.selectObjectListByQueryAs(
                    QueryWrapper.create()
                            .select(EPISODE.ID)
                            .where(EPISODE.FK_SEASON_ID.in(seasonIds)),
                    Long.class
            );

            // 存在集记录时，批量删除集
            if (ObjectUtil.isNotEmpty(episodeIds)) {
                if (episodeMapper.deleteBatchByIds(episodeIds) <= 0) {
                    throw new ServiceException("集次删除失败");
                }
            }
        }
    }

    @Override
    @CacheEvict(allEntries = true)
    public String uploadSummary(MultipartFile avatarFile, Long id) {
        // 按主键查询用户记录
        Course course = mapper.selectOneById(id);
        if (ObjectUtil.isNull(course)) {
            throw new ServiceException("用户不存在");
        }
        // 备份旧头像
        String oldAvatar = course.getSummary();
        // 重新生成文件名
        String filename = MinioUtil.randomFilename(avatarFile);
        // 根据用户主键修改用户头像
        course.setSummary(filename);
        if (mapper.update(course) <= 0) {
            throw new ServiceException("头像更新失败：DB操作异常");
        }
        try {
            // 从MinIO中删除旧头像：默认头像不删除
            if (!ML.Course.DEFAULT_SUMMARY.contains(oldAvatar)) {
                MinioUtil.delete(oldAvatar, ML.MinIO.COURSE_SUMMARY_DIR, ML.MinIO.BUCKET_NAME);
            }
            // 向MinIO中上传新头像
            MinioUtil.upload(avatarFile, filename, ML.MinIO.COURSE_SUMMARY_DIR, ML.MinIO.BUCKET_NAME);
        } catch (Exception e) {
            throw new ServiceException("头像更新失败：MinIO操作异常");
        }
        // 返回文件名
        return filename;
    }

    @Override
    @CacheEvict(allEntries = true)
    public String uploadCover(MultipartFile avatarFile, Long id) {
        // 按主键查询用户记录
        Course course = mapper.selectOneById(id);
        if (ObjectUtil.isNull(course)) {
            throw new ServiceException("用户不存在");
        }
        // 备份旧头像
        String oldAvatar = course.getCover();
        // 重新生成文件名
        String filename = MinioUtil.randomFilename(avatarFile);
        // 根据用户主键修改用户头像
        course.setCover(filename);
        if (mapper.update(course) <= 0) {
            throw new ServiceException("头像更新失败：DB操作异常");
        }
        try {
            // 从MinIO中删除旧头像：默认头像不删除
            if (!ML.Course.DEFAULT_COVER.contains(oldAvatar)) {
                MinioUtil.delete(oldAvatar, ML.MinIO.COURSE_COVER_DIR, ML.MinIO.BUCKET_NAME);
            }
            // 向MinIO中上传新头像
            MinioUtil.upload(avatarFile, filename, ML.MinIO.COURSE_COVER_DIR, ML.MinIO.BUCKET_NAME);
        } catch (Exception e) {
            throw new ServiceException("头像更新失败：MinIO操作异常");
        }
        // 返回文件名
        return filename;
    }

}
